#ifndef PORTABLE_BYTECODE_REGISTER_H_
#define PORTABLE_BYTECODE_REGISTER_H_

#include <optional>

#include "src/pbc/portable-bytecodes.h"
#include "src/pbc/portable-format.h"

namespace portable {

// Register hold parameters, this, closure, context, and expression values.
class Register final {
 public:
  constexpr explicit Register(int index = kInvalidIndex) : index_(index) {}

  constexpr int index() const { return index_; }
  constexpr bool is_parameter() const { return index() < 0; }
  constexpr bool is_valid() const { return index_ != kInvalidIndex; }

  static constexpr Register FromParameterIndex(int index);
  constexpr int ToParameterIndex() const;

  static constexpr Register receiver() { return FromParameterIndex(0); }
  constexpr bool is_receiver() const { return ToParameterIndex() == 0; }

  // Returns an invalid register.
  static constexpr Register invalid_value() { return Register(); }

  // Returns the register for the function's closure object.
  static constexpr Register function_closure();
  constexpr bool is_function_closure() const;

  // Returns the register which holds the current context object.
  static constexpr Register current_context();
  constexpr bool is_current_context() const;

  // Returns a register that can be used to represent the accumulator
  // within code, but should never be emitted in bytecode.
  static constexpr Register virtual_accumulator();

  constexpr OperandSize SizeOfOperand() const;

  constexpr int32_t ToOperand() const {
    return kRegisterFileStartOffset - index_;
  }
  static constexpr Register FromOperand(int32_t operand) {
    return Register(kRegisterFileStartOffset - operand);
  }

  static constexpr Register FromShortStar(Bytecode bytecode) {
    P_DCHECK(Bytecodes::IsShortStar(bytecode));
    return Register(static_cast<int>(Bytecode::kStar0) -
                    static_cast<int>(bytecode));
  }

#if V8_MAJOR_VERSION == 11
  static constexpr Register ToV8Register(Register reg) {
    // In version 114 compared to 132, the UnoptimizedFrame has been changed.
    // The Register with index == -2 is merged into Register with index == -1.
    // So, other registers with index < -2 need to be mapped with index + 1.
    if (reg.index() >= -1) {
      return reg;
    }
    return Register(reg.index() + 1);
  }
#endif

  constexpr std::optional<Bytecode> TryToShortStar() const {
    if (index() >= 0 && index() < Bytecodes::kShortStarCount) {
      Bytecode bytecode =
          static_cast<Bytecode>(static_cast<int>(Bytecode::kStar0) - index());
      P_DCHECK_GE(bytecode, Bytecode::kFirstShortStar);
      P_DCHECK_LE(bytecode, Bytecode::kLastShortStar);
      return bytecode;
    }
    return {};
  }

  std::string ToString() const;

  constexpr bool operator==(const Register& other) const {
    return index() == other.index();
  }
  constexpr bool operator!=(const Register& other) const {
    return index() != other.index();
  }
  constexpr bool operator<(const Register& other) const {
    return index() < other.index();
  }
  constexpr bool operator<=(const Register& other) const {
    return index() <= other.index();
  }
  constexpr bool operator>(const Register& other) const {
    return index() > other.index();
  }
  constexpr bool operator>=(const Register& other) const {
    return index() >= other.index();
  }

 private:
  static constexpr int kInvalidIndex = kInvalidRegister;

  static constexpr int kRegisterFileStartOffset = 0xfffffff9;
  static constexpr int kFirstParamRegisterIndex = 0xfffffff7;
  static constexpr int kFunctionClosureRegisterIndex = 0xfffffffb;
  static constexpr int kCurrentContextRegisterIndex = 0xfffffffa;
  static constexpr int kVirtualAccumulatorRegisterIndex = 0xfffffff8;

  int index_;
};

class RegisterList final {
 public:
  RegisterList()
      : first_reg_index_(Register::invalid_value().index()),
        register_count_(0) {}
  explicit RegisterList(Register r) : RegisterList(r.index(), 1) {}
  RegisterList(Register r, int count)
      : first_reg_index_(r.index()), register_count_(count) {}

  // Returns a new RegisterList which is a truncated version of this list, with
  // |count| registers.
  const RegisterList Truncate(int new_count) {
    P_DCHECK_GE(new_count, 0);
    P_DCHECK_LT(new_count, register_count_);
    return RegisterList(first_reg_index_, new_count);
  }
  const RegisterList PopLeft() const {
    P_DCHECK_GE(register_count_, 0);
    return RegisterList(first_reg_index_ + 1, register_count_ - 1);
  }

  const Register operator[](size_t i) const {
    P_DCHECK_LT(static_cast<int>(i), register_count_);
    return Register(first_reg_index_ + static_cast<int>(i));
  }

  const Register first_register() const {
    return (register_count() == 0) ? Register(0) : (*this)[0];
  }

  const Register last_register() const {
    return (register_count() == 0) ? Register(0) : (*this)[register_count_ - 1];
  }

  int register_count() const { return register_count_; }

 private:
  friend class BytecodeRegisterAllocator;
  friend class BytecodeDecoder;
  friend class BytecodeArrayIterator;

  RegisterList(int first_reg_index, int register_count)
      : first_reg_index_(first_reg_index), register_count_(register_count) {}

  // Increases the size of the register list by one.
  void IncrementRegisterCount() { register_count_++; }

  int first_reg_index_;
  int register_count_;
};

constexpr Register Register::FromParameterIndex(int index) {
  P_DCHECK_GE(index, 0);
  int register_index = kFirstParamRegisterIndex - index;
  P_DCHECK_LT(register_index, 0);
  return Register(register_index);
}

constexpr int Register::ToParameterIndex() const {
  P_DCHECK(is_parameter());
  return kFirstParamRegisterIndex - index();
}

constexpr Register Register::function_closure() {
  return Register(kFunctionClosureRegisterIndex);
}

constexpr bool Register::is_function_closure() const {
  return index() == kFunctionClosureRegisterIndex;
}

constexpr Register Register::current_context() {
  return Register(kCurrentContextRegisterIndex);
}

constexpr bool Register::is_current_context() const {
  return index() == kCurrentContextRegisterIndex;
}

// static
constexpr Register Register::virtual_accumulator() {
  return Register(kVirtualAccumulatorRegisterIndex);
}

constexpr OperandSize Register::SizeOfOperand() const {
  int32_t operand = ToOperand();
  if (operand >= kMinInt8 && operand <= kMaxInt8) {
    return OperandSize::kByte;
  } else if (operand >= kMinInt16 && operand <= kMaxInt16) {
    return OperandSize::kShort;
  } else {
    return OperandSize::kQuad;
  }
}

}  // namespace portable

#endif  //  PORTABLE_BYTECODE_REGISTER_H_
